// Simple passes, multiple lights
// Pixel shader.
//------------------------------------------

// Defines.
//------------------------------------------
#define MAX_LIGHTS 5
#define PI 3.14159265359

// Typedefs.
//------------------------------------------
struct Pixel
{
    float4 position             : SV_POSITION;
	float3 normal               : NORMAL;
	float3 world_view_direction : TEXCOORD0;
	float3 world_position       : TEXCOORD1;
};

struct Light
{
	float4 position;
	float3 direction;
	float1 angle;
	float1 radius;
	float3 ambient;
	float3 diffuse;
	float1 constant_attenuation;
	float3 specular;
	float1 linear_attenuation;
	float1 quadratic_attenuation;
	float1 exponent;
	float2 padding;
};

// Globals.
//------------------------------------------
cbuffer LightBuffer
{
	uint1 l_count;
	uint3 l_padding;
	Light l_lights[MAX_LIGHTS];
}

cbuffer MaterialBuffer
{
	float1 m_opacity;
	float3 m_ambient;
	float1 m_shininess;
	float3 m_diffuse;
	float3 m_specular;
	float1 m_padding;
}

float4 Main(Pixel input) : SV_TARGET
{
	// Result color.
	float3 result = float3(0.0f, 0.0f, 0.0f);

	for(uint i = 0; i < l_count; ++i)
	{
		float3 ray;
		float1 attenuation = 1.0f;
		
		// Check for directional light.
		if(l_lights[i].position.w == 0.0f)
		{
			// Normalize light direction.
			ray = normalize(l_lights[i].direction.xyz);
		}
		// Otherwise it's a point or spot light.
		else
		{
			// Get the light direction and normalize it.
			ray = input.world_position - l_lights[i].position.xyz;
			ray = normalize(ray);
			
			// Compute the distance.
			float1 distance = length(input.world_position - l_lights[i].position.xyz);
			
			// Outside range.
			if(distance > l_lights[i].radius)
				continue;
			
			// Compute the attenuation.
			attenuation = 1.0f /
						  (l_lights[i].constant_attenuation +
						   l_lights[i].linear_attenuation * distance +
						   l_lights[i].quadratic_attenuation * pow(distance, 2)); 
			
			// It's a spot light.
			if(l_lights[i].angle <= PI / 2.0f)
			{
				float1 spot_dot = max(0.0f, dot(normalize(l_lights[i].direction), ray));
				float1 cos_angle = cos(l_lights[i].angle);
				
				// Outside of spot cone.
				if(spot_dot < cos_angle)
					continue;
				
				// Soft spot edges.
				attenuation *= pow((spot_dot - cos_angle) / (1- cos_angle), l_lights[i].exponent);
			} 	
		}
		
		// Specular reflexion.		
		float1 incidence = max(0.0f, dot(input.normal, -ray.xyz));
		float1 specular_power = 0.0f;

		if(incidence > 0.0f)
		{
			float3 reflexion = normalize(reflect(ray, input.normal));
			specular_power = pow(saturate(dot(reflexion, input.world_view_direction)), m_shininess);
		}

		// Compute result.
		result += attenuation * m_ambient * l_lights[i].ambient +
				  attenuation * incidence * m_diffuse * l_lights[i].diffuse +
				  attenuation * specular_power * m_specular * l_lights[i].specular;
	}


    return float4(result, m_opacity);
}









